/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package it.softphone.commons.service;

import java.io.IOException;
import java.util.ServiceLoader;

/**
 *
 * @author bsorrentino
 */
public final class ServiceObjectFactory {

	/**
	 * lookup for a service implementation using a SPI standard
	 * 
	 * @param <S> service implementation class
	 * @param contract SPI service's contract
	 * @return service implementation class. null if no SPI service is found
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	public static <S> S getSPIService( Class<S> contract ) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException  {
        if( null==contract ) throw new IllegalArgumentException("contract is null!");

// JAVA 6
        
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        
        ServiceLoader<S> serviceLoader = ServiceLoader.load(contract, cl);
        
        try {
        	return serviceLoader.iterator().next();
        }
        catch( java.util.NoSuchElementException e ) {
        	return null;
        }
// JAVA 5        
/*        
		final String spiService = String.format( "META-INF/services/%s", contract.getName() );
			
		java.io.InputStream is = ServiceObjectFactory.class.getClassLoader().getResourceAsStream(spiService);
		
		if( is==null ) {
			logger.debug( String.format("No SPI service found! [%s]", spiService ) );
			return null;
		}
		
        java.io.LineNumberReader r = new java.io.LineNumberReader( new java.io.InputStreamReader(is));

        String fqn = r.readLine();

        ClassLoader cl = Thread.currentThread().getContextClassLoader();

        return (S)Class.forName(fqn, true, cl ).newInstance();	
*/        	
	}
	
	/**
	 * lookup for a services implementation using a SPI standard
	 * 
	 * @param <S> service implementation class
	 * @param contract SPI service's contract
	 * @return service implementation class. null if no SPI service is found
	 * @throws IOException 
	 */
	public static <S> java.util.List<S> getSPIServices( Class<S> contract )  throws IOException {
        if( null==contract ) throw new IllegalArgumentException("contract is null!");

// JAVA 6
        final java.util.List<S> result = new java.util.ArrayList<S>(5);

        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        
        ServiceLoader<S> serviceLoader = ServiceLoader.load(contract, cl);
        
        for( S service :  serviceLoader ) {
        	result.add(service);
        }
        

// JAVA 5
        
/*        
		final String spiService = String.format( "META-INF/services/%s", contract.getName() );

        Enumeration<URL> e =  ServiceObjectFactory.class.getClassLoader().getResources(spiService);
		
        if( e==null ) {
			logger.debug( String.format("No SPI service found! [%s]", spiService ) );
			return null;        	
        }

        final java.util.List<S> result = new java.util.ArrayList<S>(5);
        
        while( e.hasMoreElements() ) {
        	
            URL url = e.nextElement();
            
            try {
	    		java.io.InputStream is = url.openStream();
	
	    		if( is==null ) {
	    			logger.debug( String.format("No valid SPI service for [%s]", url ) );
	    			break;
	    		}
	
	    		java.io.LineNumberReader r = new java.io.LineNumberReader( new java.io.InputStreamReader(is));
	
	            String fqn = r.readLine();
	
	            ClassLoader cl = Thread.currentThread().getContextClassLoader();
	
	            result.add((S)Class.forName(fqn, true, cl ).newInstance());		
            }
            catch( Exception ex ) {
            	logger.warn( String.format("error loading SPI service [%s]", url )  );
            }
        }
*/		
		
        return result;
	}
	
	/**
	 * lookup for a service implementation using a given system property
	 * 
	 * @param <S> service implementation class
	 * @param systemProperty a system property that contains the FQN of service implementation class
	 * @return service implementation class
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	@SuppressWarnings("unchecked")
	public static <S> S getService( String systemProperty ) throws ClassNotFoundException, InstantiationException, IllegalAccessException   {
        if( null==systemProperty ) throw new IllegalArgumentException("systemProperty is null!");
        
        Object result = null;
    	String className = System.getProperty(systemProperty);

        if (null == className) {
            return null;
        }
            
    	ClassLoader cl = Thread.currentThread().getContextClassLoader();
    	Class<?> c = Class.forName(className, true, cl);
    
    	result = c.newInstance();
        
        return (S)result;
            
    }
    
    
    /**
	 * lookup for a service implementation using a given system property. It is possible provides a default service implementation class if property is not set or class not found
     * 
     * @param <S> service implementation class
     * @param systemProperty a system property that contains the FQN of service implementation class
     * @param defaultClass default service implementation in the case standard lookup fail
     * @return service implementation class
     * @throws IllegalAccessException 
     * @throws InstantiationException 
     * @see #getService(String)
     */
    @SuppressWarnings("unchecked")
	public static <S> S getService( String systemProperty, Class<S> defaultClass ) throws InstantiationException, IllegalAccessException  {
        if( null==defaultClass ) throw new IllegalArgumentException("defaultClass is null!");

        S result = null;
        
        if( null!=systemProperty ) {
	        
        	try {
	        	result = (S)getService(systemProperty);	        	
	        }
	        catch( ClassNotFoundException e ) {
	        	result = null;
	        }
        }        
        
        return (result==null) ? (S)defaultClass.newInstance() : result ;
    }
    
    
}
